#include <fcntl.h> 
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <sys/time.h>
#include <syslog.h>
#include <stdlib.h>

#include <netinet/ether.h>
#include <netpacket/packet.h>
#include <time.h>

typedef unsigned int u32;

typedef struct {
    u32	tag;
    u32	len;
    void *value;
}tlv;

enum {
    tag_sync_time_req = 0x0000,
    tag_sync_time_rsp,	
};

#define TIMESYNC_RETRY_INTERVAL 5
#define TIMESYNC_EXPIRES_TIME   3600


int SYSTEM(const char *format, ...)
{
#if 0
    FILE *pf,*pft;
#endif
    static char buf[4096]="";
    va_list arg;

    memset(buf, 0, sizeof(buf));

    va_start(arg, format);
    vsnprintf(buf,4096, format, arg);
    va_end(arg);
#if 0
    pf = fopen(SHELL_LOG,"a");
    if ( pf )
    {
        pft = fopen("/proc/uptime","r");
        if ( pft )
        {
#define _BUF_TM_LEN 100
            static char _buf_tm[_BUF_TM_LEN];
            fgets(_buf_tm,_BUF_TM_LEN,pft);
            fputs(_buf_tm,pf);
            fclose(pft);
        }
        fputs(buf,pf);
        fputc('\n',pf);
        fclose(pf);
    }
#endif
#ifdef  DEBUG
    printf("%s\n",buf);
#endif    
    int ret = system(buf);
    if(ret == -1) {
        syslog(LOG_INFO, "no launch: %s", buf);
    }
    else if(WIFEXITED(ret)) {
        syslog(LOG_INFO, "normal exitcode-%d: %s", WEXITSTATUS(ret), buf);
    }
    else if(WIFSIGNALED(ret)) {
        syslog(LOG_INFO, "signal-%d: %s", WTERMSIG(ret));
    }
    else {
        syslog(LOG_INFO, "*abnormal exit %d: %s", ret, buf);
    }
    usleep(1000);
    return 0;
}


/*
 * 作用: 发出时间同步请求-->接受服务器时间-->设置时间
 * return: ok: 0, failed: -1
 */
int timesync_client(char *server, unsigned short port)
{
    int ret;
    int len;
    int sockfd;   
    socklen_t addr_len;
    struct sockaddr_in addr;
    unsigned char buffer[256];
    struct timeval timeout={3, 0};

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        exit(1);
    }
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(server);
    addr_len = sizeof(struct sockaddr_in);

    if(-1==setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,(const char*)&timeout,sizeof(timeout))) {
        perror("setsockopt(SO_SNDTIMEO)\n");
        return -1;
    }
    if(-1==setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout))) {
        perror("setsockopt(SO_RCVTIMEO)\n");
        return -1;
    }

    memset(buffer, 0, sizeof(buffer));
    tlv *req = (tlv *)buffer;
    req->tag = htonl(tag_sync_time_req);
    req->len = htonl(0);
    sendto(sockfd, buffer, 8, 0, (struct sockaddr *)&addr, addr_len);

    memset(buffer, 0, sizeof(buffer));
    len = recvfrom(sockfd, buffer, sizeof(buffer), 0,
            (struct sockaddr *)&addr, &addr_len);
    close(sockfd);

    time_t time = 0;
    struct timeval tv;
    if(len > 0) {
        tlv *rsp = (tlv *)buffer;
        rsp->tag = ntohl(rsp->tag);
        rsp->len = ntohl(rsp->len);
        switch(rsp->tag) {
        case tag_sync_time_rsp:
            memcpy(&time, buffer + 8, rsp->len);
            time = ntohl(time);
            printf("rsp time=0x%08x\n", (unsigned int)time);
            tv.tv_sec = time;
            tv.tv_usec = 0;
            if((ret = settimeofday(&tv, (struct timezone *) 0)) < 0) {
                perror("settimeofday()");
            }
            break;
        default:
            break;
        }
        return 0;
    }
    else {
        return -1;
    }
}


int timesync_server(unsigned short port)
{
    int ret = 0;
    int sockfd, len = 0;
    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(struct sockaddr_in);
    unsigned char buffer[256];

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror ("socket");
        exit(1);
    }
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY) ;
    if(bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("connect()");
        exit(1);
    }

    while(1) {
        memset(buffer, 0, sizeof(buffer));
        len = recvfrom(sockfd, buffer, sizeof(buffer), 0,
                (struct sockaddr *)&addr ,&addr_len);
        if(len > 0) {
            tlv *t = (tlv *)buffer;
            t->tag = ntohl(t->tag);
            printf("Received a string from client %s,tag=0x%08x\n", inet_ntoa(addr.sin_addr), t->tag);

            switch(t->tag) {
            case tag_sync_time_req:
                {
                    time_t   t = time(NULL);
                    unsigned char rsp[256] = {0};
                    tlv *r = (tlv *)rsp;
                    r->tag = htonl(tag_sync_time_rsp);
                    r->len = htonl(sizeof(time_t));
                    printf("rsp time_t=0x%08x\n", (unsigned int )t);
                    t = htonl(t);
                    memcpy(&(r->value), &t, sizeof(t));
                    sendto(sockfd, rsp, 8+sizeof(t), 0, (struct sockaddr *)&addr, addr_len);
                }
                break;
            default:
                break;
            }
        }

#if 0
        if (0==strncmp(buffer, timesync_req_str, strlen(timesync_req_str)))
        {
            time_t   t = time(NULL);
            char rsp[256] = {0};
            sprintf(rsp, "%s:%ld", timesync_rsp_str, t);

            sendto(sockfd, rsp, strlen(rsp), 0, (struct sockaddr *)&addr, addr_len);
        }
#endif
    }

    return ret;
}

#define DEFAULT_PROD	"6801G"
#define USAGE	"timesync -s -p 9000\r\n\ttimesync -c xxx.xxx.xxx.xxx -p 9000"
/*
 *  Function : Main Function
 *  Purpose:
 *     The Main Function
 *  Parameters:
 *     argc  - Param count
 *     argv  - Param string
 *  Returns:
 *     ARP_SUCCESS  - Success
 *     ARP_FAILED   - Failure
 *
 *  Author  : zsm
 *  Date    :2016/12/22
 */
int main(int argc, char *argv[])
{
    int ret;
    int ch = 0;
    int server = 0;
    int client = 0;
    unsigned short port = 0;
    char server_ip[128] = {0};

    struct timeval tv;
    while((ch = getopt( argc, argv, "c:p:sz")) != EOF) {
        switch(ch) {
        case 's':
            server = 1;
            break;
        case 'z':
            tv.tv_sec = 0;
            tv.tv_usec = 0;
            if(settimeofday (&tv, (struct timezone *) 0) < 0) {
                printf("Set system DateTime error!\n");
            }
            printf("Set system DateTime to [1970-1-1 00:00:00]\n");
            return 0;
        case 'p':
            port = atoi(optarg);
            break;
        case 'c':
            client = 1;
            sprintf(server_ip, "%s", optarg);
            break;
        default:
            printf( "illegal option: %c\n", ch );
            printf("Usage: %s\n", USAGE);
            return 0;
        }
    }

    if(server) {
        timesync_server(port);
    }
    else if(client) {
        while(1) {
            ret = timesync_client(server_ip, port);
            if(-1==ret) {
                printf("Failed to timesync, retry after %d(s)\n", TIMESYNC_RETRY_INTERVAL);
                sleep(TIMESYNC_RETRY_INTERVAL);
            }
            else {
                printf("Succeed to timesync, next timesync after %d(s)\n", TIMESYNC_EXPIRES_TIME);
                sleep(TIMESYNC_EXPIRES_TIME);
            }
        }
    }

    return 0;
}
